package net.ufrog.common.weixin;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import jodd.http.HttpRequest;
import jodd.http.HttpResponse;
import net.ufrog.common.Logger;
import net.ufrog.common.exception.ServiceException;
import net.ufrog.common.utils.Codecs;
import net.ufrog.common.utils.Strings;
import net.ufrog.common.weixin.pojos.PaymentUnifiedOrder;
import net.ufrog.common.weixin.pojos.PaymentUnifiedOrderReturn;

import com.thoughtworks.xstream.annotations.XStreamAlias;

/**
 * 支付接口
 * 
 * @author ultrafrog
 * @version 1.0, 2015-12-03
 * @since 1.0
 */
public abstract class PaymentApis {

	/**
	 * 统一下单
	 * 
	 * @param paymentUnifiedOrder
	 * @param key
	 * @return
	 */
	public static PaymentUnifiedOrderReturn unifiedOrder(PaymentUnifiedOrder paymentUnifiedOrder, String key) {
		// 数据签名
		paymentUnifiedOrder.setSign(sign(paymentUnifiedOrder, key));
		Logger.debug("sign md5: %s", paymentUnifiedOrder.getSign());
		
		// 请求结果
		HttpResponse resp = HttpRequest.post(Apis.url(Apis.PAY_URL, "/pay/unifiedorder")).body(Apis.toXML(paymentUnifiedOrder)).send();
		Logger.debug("weixin payment unified order return: %s", resp.bodyText());
		PaymentUnifiedOrderReturn paymentUnifiedOrderReturn = Apis.fromXML(new ByteArrayInputStream(resp.bodyBytes()), PaymentUnifiedOrderReturn.class);
		
		// 验证结果
		if (checkSign(paymentUnifiedOrderReturn, key)) return paymentUnifiedOrderReturn;
		throw new ServiceException("cannot check sign.");
	}
	
	/**
	 * 解析异步通知
	 * 
	 * @param inputStream
	 * @param key
	 * @return
	 */
	public static PaymentUnifiedOrderReturn parseNotify(InputStream inputStream, String key) {
		PaymentUnifiedOrderReturn paymentUnifiedOrderReturn = Apis.fromXML(inputStream, PaymentUnifiedOrderReturn.class);
		if (checkSign(paymentUnifiedOrderReturn, key)) return paymentUnifiedOrderReturn;
		throw new ServiceException("cannot check sign.");
	}
	
	/**
	 * 脚本签名
	 * 
	 * @param mProp
	 * @return
	 */
	public static String genSignStr(Map<String, String> mProp) {
		List<String> lKeyValue = new ArrayList<String>();
		for (Entry<String, String> entry: mProp.entrySet()) {
			lKeyValue.add(entry.getKey() + "=" + entry.getValue());
		}
		Collections.sort(lKeyValue);
		return Strings.implode(lKeyValue, "&");
	}
	
	/**
	 * 签名
	 * 
	 * @param paymentUnifiedOrder
	 * @param key
	 * @return
	 */
	protected static String sign(PaymentUnifiedOrder paymentUnifiedOrder, String key) {
		String signStr = genSignStr(paymentUnifiedOrder, key);
		Logger.debug("weixin payment sign str: %s", signStr);
		return Codecs.md5(signStr + "&key=" + key).toUpperCase();
	}
	
	/**
	 * 检查签名
	 * 
	 * @param paymentUnifiedOrderReturn
	 * @param key
	 * @return
	 */
	protected static Boolean checkSign(PaymentUnifiedOrderReturn paymentUnifiedOrderReturn, String key) {
		try {
			String signStr = genSignStr(paymentUnifiedOrderReturn, key);
			Logger.debug("weixin payment return sign str: %s", signStr);
			return Codecs.md5(signStr + "&key=" + key).equalsIgnoreCase(paymentUnifiedOrderReturn.getSign());
		} catch (Exception e) {
			throw new ServiceException(e.getMessage(), e);
		}
	}
	
	/**
	 * 生成签名字符串
	 * 
	 * @param obj
	 * @param key
	 * @return
	 */
	protected static String genSignStr(Object obj, String key) {
		Map<String, String> mKeyValue = new HashMap<String, String>();
		Field[] fields = obj.getClass().getDeclaredFields();
		
		for (Field field: fields) {
			try {
				field.setAccessible(true);
				Object value = field.get(obj);
				if (value != null && !Strings.in(field.getName(), "sign", "serialVersionUID")) {
					XStreamAlias xStreamAlias = field.getAnnotation(XStreamAlias.class);
					String name = (xStreamAlias != null) ? xStreamAlias.value() : field.getName();
					mKeyValue.put(name, String.valueOf(value));
				}
			} catch (IllegalArgumentException | IllegalAccessException e) {
				throw new ServiceException(e.getMessage(), e);
			}
		}
		return genSignStr(mKeyValue);
	}
}
